/*
 * Copyright (C) 2000-2005 Philips Electronics N.V.
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or 
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; see the file gnu_license.txt.  If not, write to
 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  
 * 
 */
#include <cpp.h>
#include "type.h"
#include "gcc.h"
#include "cfront.h"

void
gcc_extenstions(void)
{
#if 0
  declarator d;
  
  internal_type ("__builtin_va_list", SIZEOF_PTR, ALIGNOF_PTR, FALSE);
  
  d = internal_declarator ("__builtin_va_end");
  declarator_type (d) = implicit_function_type;
  
  d = internal_declarator ("__builtin_va_arg");
  declarator_type (d) = implicit_function_type;
  
  d = internal_declarator ("__builtin_stdarg_start");
  declarator_type (d) = implicit_function_type;
#else
#warning TODO: verify that the following builtin #defines are right.
  char *builtins=
    "#define __builtin_va_list char *\n" /* This is slightly dangerous. */
    "#define __builtin_va_end(l)          (void)42\n"
    "#define __builtin_va_start(l,f)      l=(char*)((&f)+1)\n" /* Thrash this one? */
    "#define __builtin_stdarg_start(l,f)  l=(char*)((&f)+1)\n"
    "#define __cfront_alignof(t)          ((int)&((struct { char a; t b; }*)0)->b)\n"
    "#define __cfront_align(l,t)          l=((char*)l)+(-(unsigned int)l)%(t)\n"        /* Requires that 2^n % t == 0 (where 2^n is UINT_MAX+1). */
    "#define __cfront_align_alt(l,t)      l=(char*)(((unsigned int)l+(t)-1)/(t)*(t))\n" /* No such requirements but has very verbose expansion.  */
    "#define __builtin_va_arg(l,t)        (__cfront_align(l,__cfront_alignof(t)), l=(char*)((t*)l+1), *((t*)l-1))\n"; /* Kind of *((t*)l)++ but legal. */

#if 0
    /* Old defines: */
    "#define __builtin_va_list unsigned int\n"
    "#define __builtin_va_end\n"
    "#define __builtin_stdarg_start(l,f)  l=(int)((&f)+1)\n"
    "#define __cfront_alignof(t)          ((int)&((struct { char a; t b; }*)0)->b)\n"
    "#define __cfront_align(l,t)          l=(l)+(l-t)%(t)\n"
    "#define __builtin_va_arg(l,t)        (__cfront_align(l,__cfront_alignof(t)), *((t*)l)++)\n";
#endif

  CPP_preprocess_string("<gcc-builtin>", builtins);
#endif
}

/*
   The following restores the typedef for __gnuc_va_list to __builtin_va_list.
   This solves the warnings about 'conflicting types for builtin functions vprintf
   and vfprintf'.
*/

static void typedef_gnuc_va_list (declaration me)
{
  /* If 'me' defines __gnuc_va_list, we fill in the declaration expected by Gnu. */

  if (me)
  {
    List_declarator dtors = declaration_decls (me);
    if (dtors)
    {
      declarator dtor = List_declarator_data (dtors);
      if (dtor)
      {
        declarator idDtor = get_Id_Decl (dtor);
        if (idDtor)
        {
          Ident id = Id_Decl_id (idDtor);
          if (id && strcmp (id->name, "__gnuc_va_list") == 0)
          {
            /* Make declaration specifiers: typedef __builtin_va_list */

            char *id = "__builtin_va_list";
            declaration_specifier spec_td = Create_StorageClass (no_srcinfo, Typedef);
            declaration_specifier spec_id = Create_TypeId (no_srcinfo, Make_identifier (id));
            List_declaration_specifier sp = Create_List_declaration_specifier (spec_id, NULL);
            List_declaration_specifier specs = Create_List_declaration_specifier (spec_td, sp);
            declaration_specifiers builtin_va_list = Create_declaration_specifiers (no_srcinfo, specs);

            /* Make declarator list: __gnuc_va_list; we make sure it's a list of one Id_Decl. */

            List_declarator gnuc_va_list = Create_List_declarator (idDtor, NULL);

            /* Fill in the new stuff. */
            declaration_specs (me) = builtin_va_list;
            declaration_decls (me) = gnuc_va_list;
          }
        }
      }
    }
  }
}

void
gcc_adjust_va_list (cfront_cfront root)
{
  static struct s_void_cfront_TraverseTable restore_gnuc_va_list;
  restore_gnuc_va_list.action_declaration[PRE_ACTION] = typedef_gnuc_va_list;
  Traverse_void_cfront_cfront (root, &restore_gnuc_va_list);
}
